package com.example.manager;

import cn.hutool.json.JSONUtil;
import com.example.bean.*;
import com.example.constant.TopicConst;
import com.example.utils.GeoUtils;
import com.example.utils.MqttUtil;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;

/**
 * 雷达管理器
 *
 * @author: Fred
 * @email 453086@qq.com
 * @create: 2024-09-29 14:23
 */
@Slf4j
public class RadarManager {

    private static final RadarManager instance = new RadarManager();

    private final Map<Long, RadarTarget> radarTargetMap = new ConcurrentHashMap<>();
    private final Map<String, List<RadarTarget>> radarDataHistory = new ConcurrentHashMap<>();

    private final Map<Long, List<RadarFrame>> radarFrameBuffer = new ConcurrentHashMap<>();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private RadarManager() {
        scheduler.scheduleAtFixedRate(this::processRadarFrames, 1, 1, TimeUnit.SECONDS);
    }

    public static RadarManager getInstance() {
        return instance;
    }

    public RadarTarget getRadarTarget(Long targetSn) {
        return radarTargetMap.get(targetSn);
    }

    /**
     * 更新或添加新的船舶位置
     */
    public Map<String, List<RadarTarget>> getRadarDataHistory() {
        return radarDataHistory;
    }

    public void removeRadarDataHistory(String key) {
        radarDataHistory.remove(key);
    }

    public void ParseRadarMessage(byte[] data, DeviceConf deviceConf) {
        // 目标数据解析
        int offset = 0;
        if (data[0] == (byte) 0x55 && data[1] == (byte) 0xAA) {

            offset += 4;
            offset += 1;
            int targetsCount = data[offset] & 0xFF;
            offset += 1;

            for (int j = 0; j < targetsCount; j++) {
                int curIndex = offset + j * 17;
                RadarFrame frame = RadarFrame.builder()
                        .id(data[curIndex] & 0xFF)
                        .y(shortLE(data, curIndex + 1) * 0.1F)
                        .x(shortLE(data, curIndex + 3) * 0.1F)
                        .v(shortLE(data, curIndex + 5) * 0.1F)
                        .range(data[curIndex + 7] & 0xFF)
                        .snr(data[curIndex + 8] & 0xFF)
                        .rcs(shortLE(data, curIndex + 9))
                        .pointCount(data[curIndex + 11] & 0xFF)
                        .length(data[curIndex + 12] & 0xFF)
                        .radarLat(deviceConf.getLatitude())
                        .radarLon(deviceConf.getLongitude())
                        .azimuth(deviceConf.getAzimuth())
                        .secret(deviceConf.getSecret())
                        .build();



                // 这里可以处理 radarFrames 列表，例如打印或者存储
                if (frame.getSnr() > 35) {
                    radarFrameBuffer.computeIfAbsent(frame.getId(), k -> new ArrayList<>()).add(frame);
                }
            }
        }
    }

    private void processRadarFrames() {
        radarFrameBuffer.forEach((id, frames) -> {
            try {
                RadarFrame averagedFrame = calculateAverage(frames);
                if (averagedFrame != null) {
                    log.info(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + " 收到雷达原始数据：" + JSONUtil.toJsonStr(averagedFrame));
                    RadarTarget radarTarget = createRadarTarget(averagedFrame);
                    MqttUtil.mqttTemplate.publishAsync(TopicConst.RADAR_POINT + radarTarget.getTargetSn(), JSONUtil.toJsonStr(radarTarget).getBytes(StandardCharsets.UTF_8));
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        radarFrameBuffer.clear();
    }

    private RadarFrame calculateAverage(List<RadarFrame> frames) {
        RadarFrame frame = frames.get(0);
        String secret = frame.getSecret();
        long id = Long.parseLong(String.format("%s%03d", secret, frame.getId()));

        return RadarFrame.builder()
                .id(id)
                .y((float) frames.stream().mapToDouble(RadarFrame::getY).average().orElse(0.0))
                .x((float) frames.stream().mapToDouble(RadarFrame::getX).average().orElse(0.0))
                .v((float) frames.stream().mapToDouble(RadarFrame::getV).average().orElse(0.0))
                .range((int) frames.stream().mapToInt(RadarFrame::getRange).average().orElse(0))
                .snr((int) frames.stream().mapToInt(RadarFrame::getSnr).average().orElse(0))
                .rcs((int) frames.stream().mapToInt(RadarFrame::getRcs).average().orElse(0))
                .pointCount((int) frames.stream().mapToInt(RadarFrame::getPointCount).average().orElse(0))
                .length((int) frames.stream().mapToInt(RadarFrame::getLength).average().orElse(0))
                .radarLat(frame.getRadarLat())
                .radarLon(frame.getRadarLon())
                .azimuth(frame.getAzimuth())
                .secret(secret)
                .build();
    }

    public RadarTarget createRadarTarget(RadarFrame frame) {

        long targetSn = frame.getId();
        // 目标的距离和角度变化
        double distance = Math.sqrt(frame.getX() * frame.getX() + frame.getY() * frame.getY());
        double angle = Math.atan2(frame.getX(), frame.getY()) + Math.toRadians(frame.getAzimuth());

        // 目标的经纬度变化
        double deltaLat = (distance * Math.cos(angle)) / 111120;
        double deltaLon = (distance * Math.sin(angle)) / (111120 * Math.cos(Math.toRadians(frame.getRadarLat())));

        // 计算目标的经纬度
        double targetLat = frame.getRadarLat() + deltaLat;
        double targetLon = frame.getRadarLon() + deltaLon;

        // 3. 计算航向和速度
        double direction = calculateDirection(targetSn, targetLat, targetLon);
        double speed = Math.abs(frame.getV() / Math.cos(Math.toRadians(frame.getAzimuth()))) * 3600 / 1852;

        String dataTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        RadarTarget target = RadarTarget.builder()
                .targetSn(targetSn).latitude(targetLat).longitude(targetLon).distance(distance).course(direction).speed(speed).rcs(frame.getRcs()).length(frame.getLength()).pointCount(frame.getPointCount()).secret(frame.getSecret()).dataTime(dataTime).build();

        radarTargetMap.put(target.getTargetSn(), target);
        radarDataHistory.computeIfAbsent(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")), k -> new ArrayList<>()).add(target);

        return target;
    }

    private double calculateDirection(long targetSn, double targetLat, double targetLon) {
        RadarTarget previousTarget = getRadarTarget(targetSn);
        return previousTarget == null ? 0 : GeoUtils.calculateBearing(previousTarget.getLatitude(), previousTarget.getLongitude(), targetLat, targetLon);
    }

    private short shortLE(byte[] data, int index) {
        return (short) ((data[index] & 0xFF) | ((data[index + 1] & 0xFF) << 8));
    }
}